﻿using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using O1;
using O1.Data;
using O1.Data.Traversal;
using O1.Data.Traversal.Fakes;

namespace Tests.O1.Data.Traversal
{
    [TestClass]
    public class CellInterpreterTests
    {
        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterAlwaysInvokesObserverAndReturns0ByteCountForVertexEnd()
        {
            byte[] buffer = new byte[Edge.Size];
            new Edge().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EndVertexObservationInterests = s => { count++; return ObservationInterests.Interested; };
            var observer = new CellObserver(filter);

            CellInterpreter.Interpret(Markup.VertexEnd, buffer, 0, observer).Should().Be(0);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterAlwaysInvokesObserverAndReturns0ByteCountForEdgeEnd()
        {
            byte[] buffer = new byte[Edge.Size];
            new Edge().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EndEdgeObservationInterests = s => { count++; return ObservationInterests.Interested; };
            var observer = new CellObserver(filter);

            CellInterpreter.Interpret(Markup.EdgeEnd, buffer, 0, observer).Should().Be(0);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForEdgeInWhenInterested()
        {
            byte[] buffer = new byte[Edge.Size];
            new Edge().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.StartEdgeEdgeEdgeDirections = (e, d) => { count++; return ObservationInterests.Interested; };
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.InEdge, buffer, 0, observer).Should().Be(Edge.Size);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForEdgeInWhenUninterested()
        {
            byte[] buffer = new byte[Edge.Size];
            new Edge().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.StartEdgeEdgeEdgeDirections = (e, d) => { count++; return ObservationInterests.Interested; };
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.InEdge, buffer, 0, observer).Should().Be(Edge.Size);
            count.Should().Be(0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForEdgeOutWhenInterested()
        {
            byte[] buffer = new byte[Edge.Size];
            new Edge().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.StartEdgeEdgeEdgeDirections = (e, d) => { count++; return ObservationInterests.Interested; };
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.OutEdge, buffer, 0, observer).Should().Be(Edge.Size);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForEdgeOutWhenUninterested()
        {
            byte[] buffer = new byte[Edge.Size];
            new Edge().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.StartEdgeEdgeEdgeDirections = (e, d) => { count++; return ObservationInterests.Interested; };
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.OutEdge, buffer, 0, observer).Should().Be(Edge.Size);
            count.Should().Be(0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForDatePropertyWhenInterested()
        {
            byte[] buffer = new byte[DateProperty.Size];
            new DateProperty().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateDateProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.DateProp, buffer, 0, observer).Should().Be(DateProperty.Size);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForDatePropertyWhenUninterested()
        {
            byte[] buffer = new byte[DateProperty.Size];
            new DateProperty().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateDateProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.DateProp, buffer, 0, observer).Should().Be(DateProperty.Size);
            count.Should().Be(0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForDateTimePropertyWhenInterested()
        {
            byte[] buffer = new byte[DateTimeProperty.Size];
            new DateTimeProperty().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateDateTimeProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.DateTimeProp, buffer, 0, observer).Should().Be(DateTimeProperty.Size);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForDateTimePropertyWhenUninterested()
        {
            byte[] buffer = new byte[DateTimeProperty.Size];
            new DateTimeProperty().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateDateTimeProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.DateTimeProp, buffer, 0, observer).Should().Be(DateTimeProperty.Size);
            count.Should().Be(0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForDecimalPropertyWhenInterested()
        {
            byte[] buffer = new byte[DecimalProperty.Size];
            new DecimalProperty().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateDecimalProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.DecimalProp, buffer, 0, observer).Should().Be(DecimalProperty.Size);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForDecimalPropertyWhenUninterested()
        {
            byte[] buffer = new byte[DecimalProperty.Size];
            new DecimalProperty().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateDecimalProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.DecimalProp, buffer, 0, observer).Should().Be(DecimalProperty.Size);
            count.Should().Be(0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForHashPropertyWhenInterested()
        {
            byte[] buffer = new byte[HashProperty.Size];
            new HashProperty().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateHashProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.HashProp, buffer, 0, observer).Should().Be(HashProperty.Size);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForHashPropertyWhenUninterested()
        {
            byte[] buffer = new byte[HashProperty.Size];
            new HashProperty().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateHashProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.HashProp, buffer, 0, observer).Should().Be(HashProperty.Size);
            count.Should().Be(0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForInt16PropertyWhenInterested()
        {
            byte[] buffer = new byte[Int16Property.Size];
            new Int16Property().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateInt16Property = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.Int16Prop, buffer, 0, observer).Should().Be(Int16Property.Size);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForInt16PropertyWhenUninterested()
        {
            byte[] buffer = new byte[Int16Property.Size];
            new Int16Property().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateInt16Property = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.Int16Prop, buffer, 0, observer).Should().Be(Int16Property.Size);
            count.Should().Be(0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForInt32PropertyWhenInterested()
        {
            byte[] buffer = new byte[Int32Property.Size];
            new Int32Property().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateInt32Property = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.Int32Prop, buffer, 0, observer).Should().Be(Int32Property.Size);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForInt32PropertyWhenUninterested()
        {
            byte[] buffer = new byte[Int32Property.Size];
            new Int32Property().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateInt32Property = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.Int32Prop, buffer, 0, observer).Should().Be(Int32Property.Size);
            count.Should().Be(0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForInt64PropertyWhenInterested()
        {
            byte[] buffer = new byte[Int64Property.Size];
            new Int64Property().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateInt64Property = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.Int64Prop, buffer, 0, observer).Should().Be(Int64Property.Size);
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForInt64PropertyWhenUninterested()
        {
            byte[] buffer = new byte[Int64Property.Size];
            new Int64Property().Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateInt64Property = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.Int64Prop, buffer, 0, observer).Should().Be(Int64Property.Size);
            count.Should().Be(0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterInvokesObserverAndReturnsByteCountForStringPropertyWhenInterested()
        {
            var value = "thistest";
            var property = StringProperty.Create(99, value);
            byte[] buffer = new byte[StringProperty.FixedSize + value.Length];
            property.Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateStringProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.Interested;

            CellInterpreter.Interpret(Markup.StringProp, buffer, 0, observer).Should().Be(property.GetSize());
            count.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellInterpreterDoesNotInvokeObserverAndReturnsByteCountForStringPropertyWhenUninterested()
        {
            var value = "thistest";
            var property = StringProperty.Create(99, value);
            byte[] buffer = new byte[StringProperty.FixedSize + value.Length];
            property.Write(buffer, 0);

            var count = 0;
            var filter = new StubFilter();
            filter.EvaluateStringProperty = (v) => { count++; return ObservationInterests.Interested; };
            filter.PropertyInterestGet = () => PropertyInterests.All;
            var observer = new CellObserver(filter);
            observer.ObservationState = ObservationInterests.None;

            CellInterpreter.Interpret(Markup.StringProp, buffer, 0, observer).Should().Be(property.GetSize());
            count.Should().Be(0);
        }
    }
}
